package com.xyz.study.netty.cases.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * TimeServer
 *
 * @author xyz
 * @version 1.0.0
 * @since 2021/7/1
 */
public class TimeServer {

    private void bind(int port){
        // 创建反应器轮询线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 1.设置反应器轮询组
        serverBootstrap.group(bossGroup, workerGroup)
                // 2.设置IO类型
                .channel(NioServerSocketChannel.class)
                // 3.bind port
                .localAddress(port)
                /**
                 * 对于ChannelOption.SO_BACKLOG的解释：
                 * 服务器端TCP内核维护有两个队列，我们称之为A、B队列。客户端向服务器端connect时，会发送带有SYN标志的包（第一次握手），服务器端
                 * 接收到客户端发送的SYN时，向客户端发送SYN ACK确认（第二次握手），此时TCP内核模块把客户端连接加入到A队列中，然后服务器接收到
                 * 客户端发送的ACK时（第三次握手），TCP内核模块把客户端连接从A队列移动到B队列，连接完成，应用程序的accept会返回。也就是说accept
                 * 从B队列中取出完成了三次握手的连接。
                 * A队列和B队列的长度之和就是backlog。当A、B队列的长度之和大于ChannelOption.SO_BACKLOG时，新的连接将会被TCP内核拒绝。
                 * 所以，如果backlog过小，可能会出现accept速度跟不上，A、B队列满了，导致新的客户端无法连接。要注意的是，backlog对程序支持的
                 * 连接数并无影响，backlog影响的只是还没有被accept取出的连接
                 */
                // 4.设置通道参数
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 5.装配子通道流水线
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(
                                new StringEncoder(CharsetUtil.UTF_8), // 字符串编码
                                new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()), // 以("\n")为结尾分割的 解码器
                                new StringDecoder(CharsetUtil.UTF_8), // 字符串解码
                                new TimeServerHandler()
                        );
                    }
                });
        try {
            // 6.绑定服务器
            // 通过调用sync同步方法阻塞直到绑定成功
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            // 7.等待通道关闭
            // 通过调用sync同步方法，同步等待通道关闭的异步任务结束
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
            // 8.优雅关闭EventLoopGroup
            // 释放掉所有资源，包括创建的线程
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (null != args && args.length > 0){
            try{
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e){
                e.printStackTrace();
            }
        }

        new TimeServer().bind(port);
    }
}
